/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package co.edu.unal.bioingenium.kbmed.knowledge.similarity.main;

import co.edu.unal.bioingenium.kbmed.config.Configuration;
import co.edu.unal.bioingenium.kbmed.knowledge.dao.KnowledgeSourceDAO;
import co.edu.unal.bioingenium.kbmed.knowledge.dao.KnowledgeSourceDAOFactory;
import co.edu.unal.bioingenium.kbmed.knowledge.hierarchy.HierarchyService;
import co.edu.unal.bioingenium.kbmed.knowledge.hierarchy.vo.Vertex;
import co.edu.unal.bioingenium.kbmed.util.io.FileManager;
import co.edu.unal.bioingenium.kbmed.util.math.UtilMath;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * IC (a)= -log((|leaves(a)|/|subsummers(a))|+1/(max_levaes+1))
 * Where: 
 * leaves(a) = {l e C | l is a hyponyms of a AND l is a leaf} where l is leaf iff hyponyms(l)=0 
 * subsumers(a) = {c e C | a is a hierarchical specialization of c} U {a}
 * max_leaves = the number of leaves corresponding to root (all leaves)
 *  
 * @author Alejandro Riveros Cruz
 */
public class CalculateInformationContent {

    private HierarchyService hierarchyManager;
    private Map<String, Set<String>> subsumersMap;
    private Map<String, Set<String>> leavesMap;

    /**
     * 
     */
    public CalculateInformationContent() {
        hierarchyManager = HierarchyService.getInstance();

    }

    /**
     * 
     * @param category
     */
    public void doCount(String category) {
        hierarchyManager.clearAllHierarchies();
        hierarchyManager.initHierarchy(category);
        leavesMap = new HashMap<String, Set<String>>();
        subsumersMap = new HashMap<String, Set<String>>();
        countLeaves(category);
        countSubsumers(category);
        saveICCountersAndValues(category);

    }

    /**
     * 
     * @param category
     */
    private void countLeaves(String category) {
        Logger.getLogger(CalculateInformationContent.class.getName()).log(Level.INFO, "Counting leaves for category \"{0}\"", category);
        countVertexLeaves(category, hierarchyManager.getHierarchy(category).getTopConceptId(), new HashSet<String>());
        Logger.getLogger(CalculateInformationContent.class.getName()).log(Level.INFO, "Counting leaves for category \"{0}\" OK.", category);
    }

    private void countVertexLeaves(String category, String conceptId, Set<String> parentLeaves) {
        Set<String> currentLeaves;
        if (!leavesMap.containsKey(conceptId)) { //If leaves set if is not created
            currentLeaves = new HashSet<String>(); //Create new leaves set
            leavesMap.put(conceptId, currentLeaves);// Add to leaves map
        } else {
            currentLeaves = leavesMap.get(conceptId);//Get leaves set
        }
        //Validate if call more recursion
        if (hierarchyManager.getInDegree(category, conceptId) == 0) {//Is leave
            parentLeaves.add(conceptId);
            return;//Recursion end
        } else {
            Set<String> childrens = hierarchyManager.getDirectChildrens(category, conceptId);
            for (String children : childrens) {//For each children
                countVertexLeaves(category, children, currentLeaves); //Call the recursive method
            }
            parentLeaves.addAll(currentLeaves); // add this children as a leave
        }



    }

    /**
     * 
     * @param category
     */
    private void countSubsumers(String category) {
        Logger.getLogger(CalculateInformationContent.class.getName()).log(Level.INFO, "Counting subsumers for category \"{0}\"", category);
        countVertexSubsumers(category, hierarchyManager.getHierarchy(category).getTopConceptId(), new HashSet<String>());
        Logger.getLogger(CalculateInformationContent.class.getName()).log(Level.INFO, "Counting subsumers for category \"{0}\" OK.", category);
    }

    private void countVertexSubsumers(String category, String conceptId, Set<String> parentSubsumers) {
        Set<String> currentSubsummers;
        if (!subsumersMap.containsKey(conceptId)) { //If subsemmers set if is not created
            currentSubsummers = new HashSet<String>(); //Create new subsumers set
            subsumersMap.put(conceptId, currentSubsummers);// Add to subsumers map
        } else {
            currentSubsummers = subsumersMap.get(conceptId);//Get subsumers
        }
        currentSubsummers.addAll(parentSubsumers);// Add the parent subsumers
        currentSubsummers.add(conceptId);//Add current concept as subsummer of itself
        //Validate if call more recursion
        if (hierarchyManager.getInDegree(category, conceptId) == 0) {//Is leave
            return;//Recursion end
        } else {
            Set<String> childrens = hierarchyManager.getDirectChildrens(category, conceptId);
            for (String children : childrens) {//For each children
                countVertexSubsumers(category, children, currentSubsummers); //Call the recursive method
            }
        }
    }

    private void saveICCountersAndValues(String category) {
        try {
            String categoryStandard = FileManager.getStandardName(category);
            Logger.getLogger(CalculateInformationContent.class.getName()).log(Level.INFO, "Store ICcounters \"{0}\"", category);
            BufferedWriter informationContentValsBF = new BufferedWriter(new FileWriter(new File(Configuration.FEATURE_DIR + File.separator + Configuration.KNOWLEDGE_SOURCE_NAME.toLowerCase() + "-" + categoryStandard + "InformationContentValues.cvs")));
            BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(new File(Configuration.DATA_DIR + File.separator + Configuration.KNOWLEDGE_SOURCE_NAME.toLowerCase() + "-" + categoryStandard + "InformationContentDistances.txt")));
            bufferedWriter.write("Category: " + category + "\n");
            bufferedWriter.write("conceptId"
                    + "\tconceptDepth"
                    + "\tnumParents"
                    + "\tnumChildrens"
                    + "\tnumSiblings"
                    + "\tsubsumersCount"
                    + "\tleavesCount"
                    + "\tICraw\n");
            int maxLeaves = hierarchyManager.getHierarchy(category).getLeavesSet().size();
            float scale = (float) -UtilMath.log2((float) 1 / (float) (maxLeaves + 1));
            Map<String, Vertex> vertexMap = hierarchyManager.getHierarchy(category).getVertexMap();
            Map<Vertex, Number> depthMap = hierarchyManager.getHierarchy(category).getDepthMap();
            float IC;
            int conceptDepth, numParents, numChildrens, numSiblings;
            for (String conceptId : subsumersMap.keySet()) {
                numParents = hierarchyManager.getDirectParents(category, conceptId).size();
                numChildrens = hierarchyManager.getDirectChildrens(category, conceptId).size();
                numSiblings = hierarchyManager.getDirectSiblings(category, conceptId).size();
                conceptDepth = depthMap.get(vertexMap.get(conceptId)).intValue() + 1;
                if (leavesMap.get(conceptId).size() > 0) {//Is node
                    IC = (float) (-1 * UtilMath.log2((((float) leavesMap.get(conceptId).size() / (float) subsumersMap.get(conceptId).size()) + 1) / (float) (maxLeaves + 1)));
                    bufferedWriter.write(conceptId
                            + "\t" + conceptDepth
                            + "\t" + numParents
                            + "\t" + numChildrens
                            + "\t" + numSiblings
                            + "\t" + subsumersMap.get(conceptId).size()
                            + "\t" + leavesMap.get(conceptId).size()
                            + "\t" + IC + "\n");
                } else {//Is leaf
                    IC = (float) (-1 * UtilMath.log2((((float) 0 / (float) subsumersMap.get(conceptId).size()) + 1) / (float) (maxLeaves + 1)));
                    bufferedWriter.write(conceptId
                            + "\t" + conceptDepth
                            + "\t" + numParents
                            + "\t" + numChildrens
                            + "\t" + numSiblings
                            + "\t" + subsumersMap.get(conceptId).size()
                            + "\t0"
                            + "\t" + IC + "\n");
                }
                //saving IC values
                informationContentValsBF.write(conceptId + "," + (float) (IC / scale) + "\n");
            }
            informationContentValsBF.flush();
            informationContentValsBF.close();
            bufferedWriter.flush();
            bufferedWriter.close();
        } catch (IOException ex) {
            Logger.getLogger(CalculateInformationContent.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * 
     * @param args
     */
    public static void main(String[] args) {
        CalculateInformationContent ic = new CalculateInformationContent();
        KnowledgeSourceDAO knowledgeSourceDAO = KnowledgeSourceDAOFactory.getKnowledgeSourceDAO();
        Set<String> categories = knowledgeSourceDAO.getCategories();
        for (String category : categories) {
            ic.doCount(category);
        }
    }
}
